using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Core.Address;
using HslCommunication.Serial;

namespace HslCommunication.ModBus
{
	/// <summary>
	/// Modbus-Rtu通讯协议的类库，多项式码0xA001，支持标准的功能码，也支持扩展的功能码实现，地址采用富文本的形式，详细见备注说明<br />
	/// Modbus-Rtu communication protocol class library, polynomial code 0xA001, supports standard function codes, 
	/// and also supports extended function code implementation. The address is in rich text. For details, see the remark
	/// </summary>
	/// <remarks>
	/// <inheritdoc cref="T:HslCommunication.ModBus.ModbusTcpNet" path="remarks" />
	/// </remarks>
	/// <example>
	/// <inheritdoc cref="T:HslCommunication.ModBus.ModbusTcpNet" path="example" />
	/// </example>
	public class ModbusRtu : SerialDeviceBase
	{
		private byte station = 1;

		private bool isAddressStartWithZero = true;

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.AddressStartWithZero" />
		public bool AddressStartWithZero
		{
			get
			{
				return isAddressStartWithZero;
			}
			set
			{
				isAddressStartWithZero = value;
			}
		}

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.Station" />
		public byte Station
		{
			get
			{
				return station;
			}
			set
			{
				station = value;
			}
		}

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.DataFormat" />
		public DataFormat DataFormat
		{
			get
			{
				return base.ByteTransform.DataFormat;
			}
			set
			{
				base.ByteTransform.DataFormat = value;
			}
		}

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.IsStringReverse" />
		public bool IsStringReverse
		{
			get
			{
				return base.ByteTransform.IsStringReverseByteWord;
			}
			set
			{
				base.ByteTransform.IsStringReverseByteWord = value;
			}
		}

		/// <summary>
		/// 实例化一个Modbus-Rtu协议的客户端对象<br />
		/// Instantiate a client object of the Modbus-Rtu protocol
		/// </summary>
		public ModbusRtu()
		{
			base.ByteTransform = new ReverseWordTransform();
		}

		/// <summary>
		/// 指定客户端自己的站号来初始化<br />
		/// Specify the client's own station number to initialize
		/// </summary>
		/// <param name="station">客户端自身的站号</param>
		public ModbusRtu(byte station = 1)
		{
			this.station = station;
			base.ByteTransform = new ReverseWordTransform();
		}

		/// <summary>
		/// 检查当前的Modbus-Rtu响应是否是正确的<br />
		/// Check if the current Modbus-Rtu response is correct
		/// </summary>
		/// <param name="send">发送的数据信息</param>
		/// <returns>带是否成功的结果数据</returns>
		protected virtual OperateResult<byte[]> CheckModbusTcpResponse(byte[] send)
		{
			send = ModbusInfo.PackCommandToRtu(send);
			OperateResult<byte[]> operateResult = ReadBase(send);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			if (operateResult.Content.Length < 5)
			{
				return new OperateResult<byte[]>(StringResources.Language.ReceiveDataLengthTooShort + "5");
			}
			if (!SoftCRC16.CheckCRC16(operateResult.Content))
			{
				return new OperateResult<byte[]>(StringResources.Language.ModbusCRCCheckFailed + SoftBasic.ByteToHexString(operateResult.Content, ' '));
			}
			if (send[1] + 128 == operateResult.Content[1])
			{
				return new OperateResult<byte[]>(operateResult.Content[2], ModbusInfo.GetDescriptionByErrorCode(operateResult.Content[2]));
			}
			if (send[1] != operateResult.Content[1])
			{
				return new OperateResult<byte[]>(operateResult.Content[1], "Receive Command Check Failed: ");
			}
			return ModbusInfo.ExtractActualData(ModbusInfo.ExplodeRtuCommandToCore(operateResult.Content));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadModBus(HslCommunication.Core.Address.ModbusAddress,System.UInt16)" />
		private OperateResult<byte[]> ReadModBus(ModbusAddress address, ushort length)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildReadModbusCommand(address, length);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadCoil(System.String)" />
		public OperateResult<bool> ReadCoil(string address)
		{
			return ReadBool(address);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadCoil(System.String,System.UInt16)" />
		public OperateResult<bool[]> ReadCoil(string address, ushort length)
		{
			return ReadBool(address, length);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadDiscrete(System.String)" />
		public OperateResult<bool> ReadDiscrete(string address)
		{
			return ByteTransformHelper.GetResultFromArray(ReadDiscrete(address, 1));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadDiscrete(System.String,System.UInt16)" />
		public OperateResult<bool[]> ReadDiscrete(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildReadModbusCommand(address, length, Station, AddressStartWithZero, 2);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = CheckModbusTcpResponse(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult2);
			}
			return OperateResult.CreateSuccessResult(operateResult2.Content.ToBoolArray(length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Read(System.String,System.UInt16)" />
		public override OperateResult<byte[]> Read(string address, ushort length)
		{
			OperateResult<ModbusAddress> operateResult = ModbusInfo.AnalysisAddress(address, Station, isAddressStartWithZero, 3);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			List<byte> list = new List<byte>();
			ushort num = 0;
			while (num < length)
			{
				ushort num2 = (ushort)Math.Min(length - num, 120);
				OperateResult<byte[]> operateResult2 = ReadModBus(operateResult.Content.AddressAdd(num), num2);
				if (!operateResult2.IsSuccess)
				{
					return OperateResult.CreateFailedResult<byte[]>(operateResult2);
				}
				list.AddRange(operateResult2.Content);
				num = (ushort)(num + num2);
			}
			return OperateResult.CreateSuccessResult(list.ToArray());
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.Byte[])" />
		public override OperateResult Write(string address, byte[] value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 16);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.Int16)" />
		public override OperateResult Write(string address, short value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 6);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.UInt16)" />
		public override OperateResult Write(string address, ushort value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 6);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteMask(System.String,System.UInt16,System.UInt16)" />
		public OperateResult WriteMask(string address, ushort andMask, ushort orMask)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteMaskModbusCommand(address, andMask, orMask, Station, AddressStartWithZero, 22);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.Write(System.String,System.Int16)" />
		public OperateResult WriteOneRegister(string address, short value)
		{
			return Write(address, value);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.Write(System.String,System.UInt16)" />
		public OperateResult WriteOneRegister(string address, ushort value)
		{
			return Write(address, value);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.Write(System.String,System.Int16)" />/param&gt;
		public override async Task<OperateResult> WriteAsync(string address, short value)
		{
			return await Task.Run(() => Write(address, value));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.Write(System.String,System.UInt16)" />/param&gt;
		public override async Task<OperateResult> WriteAsync(string address, ushort value)
		{
			return await Task.Run(() => Write(address, value));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.ReadCoil(System.String)" />
		public async Task<OperateResult<bool>> ReadCoilAsync(string address)
		{
			return await Task.Run(() => ReadCoil(address));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.ReadCoil(System.String,System.UInt16)" />
		public async Task<OperateResult<bool[]>> ReadCoilAsync(string address, ushort length)
		{
			return await Task.Run(() => ReadCoil(address, length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.ReadDiscrete(System.String)" />
		public async Task<OperateResult<bool>> ReadDiscreteAsync(string address)
		{
			return await Task.Run(() => ReadDiscrete(address));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.ReadDiscrete(System.String,System.UInt16)" />
		public async Task<OperateResult<bool[]>> ReadDiscreteAsync(string address, ushort length)
		{
			return await Task.Run(() => ReadDiscrete(address, length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.WriteOneRegister(System.String,System.Int16)" />
		public async Task<OperateResult> WriteOneRegisterAsync(string address, short value)
		{
			return await Task.Run(() => WriteOneRegister(address, value));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.WriteOneRegister(System.String,System.UInt16)" />
		public async Task<OperateResult> WriteOneRegisterAsync(string address, ushort value)
		{
			return await Task.Run(() => WriteOneRegister(address, value));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.WriteMask(System.String,System.UInt16,System.UInt16)" />
		public async Task<OperateResult> WriteMaskAsync(string address, ushort andMask, ushort orMask)
		{
			return await Task.Run(() => WriteMask(address, andMask, orMask));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadBool(System.String,System.UInt16)" />
		public override OperateResult<bool[]> ReadBool(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildReadModbusCommand(address, length, Station, AddressStartWithZero, 1);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = CheckModbusTcpResponse(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult2);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(operateResult2.Content, length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.Boolean[])" />
		public override OperateResult Write(string address, bool[] values)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteBoolModbusCommand(address, values, Station, AddressStartWithZero, 15);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.Boolean)" />
		public override OperateResult Write(string address, bool value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteBoolModbusCommand(address, value, Station, AddressStartWithZero, 5);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.Write(System.String,System.Boolean)" />
		public override async Task<OperateResult> WriteAsync(string address, bool value)
		{
			return await Task.Run(() => Write(address, value));
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return $"ModbusRtu[{base.PortName}:{base.BaudRate}]";
		}
	}
}
